#include <kutils.h>
#include "display.h"
#include "memory.h"
#include "irq.h"
#include "idt.h"
#include "task.h"

void fault_handler(struct regs *r);

extern u32int placement_address;
extern task_t* current_task;
u32int recursions = 0;

/* These are function prototypes for all of the exception
*  handlers: The first 32 entries in the IDT are reserved
*  by Intel, and are designed to service exceptions! */
extern void isr0(void);
extern void isr1(void);
extern void isr2(void);
extern void isr3(void);
extern void isr4(void);
extern void isr5(void);
extern void isr6(void);
extern void isr7(void);
extern void isr8(void);
extern void isr9(void);
extern void isr10(void);
extern void isr11(void);
extern void isr12(void);
extern void isr13(void);
extern void isr14(void);
extern void isr15(void);
extern void isr16(void);
extern void isr17(void);
extern void isr18(void);
extern void isr19(void);
extern void isr20(void);
extern void isr21(void);
extern void isr22(void);
extern void isr23(void);
extern void isr24(void);
extern void isr25(void);
extern void isr26(void);
extern void isr27(void);
extern void isr28(void);
extern void isr29(void);
extern void isr30(void);
extern void isr31(void);

/* This is a very repetitive function... it's not hard, it's
*  just annoying. As you can see, we set the first 32 entries
*  in the IDT to the first 32 ISRs. We can't use a for loop
*  for this, because there is no way to get the function names
*  that correspond to that given entry. We set the access
*  flags to 0x8E. This means that the entry is present, is
*  running in ring 0 (kernel level), and has the lower 5 bits
*  set to the required '14', which is represented by 'E' in
*  hex. */
void isrs_install(void)
{
#ifdef FLUIDIUM_DEBUG
	write_string("ISRS: Init\n");
#endif
    idt_set_gate(0, (unsigned)isr0, 0x08, 0x8E);
    idt_set_gate(1, (unsigned)isr1, 0x08, 0x8E);
    idt_set_gate(2, (unsigned)isr2, 0x08, 0x8E);
    idt_set_gate(3, (unsigned)isr3, 0x08, 0x8E);
    idt_set_gate(4, (unsigned)isr4, 0x08, 0x8E);
    idt_set_gate(5, (unsigned)isr5, 0x08, 0x8E);
    idt_set_gate(6, (unsigned)isr6, 0x08, 0x8E);
    idt_set_gate(7, (unsigned)isr7, 0x08, 0x8E);
    idt_set_gate(8, (unsigned)isr8, 0x08, 0x8E);
    idt_set_gate(9, (unsigned)isr9, 0x08, 0x8E);
    idt_set_gate(10, (unsigned)isr10, 0x08, 0x8E);
    idt_set_gate(11, (unsigned)isr11, 0x08, 0x8E);
    idt_set_gate(12, (unsigned)isr12, 0x08, 0x8E);
    idt_set_gate(13, (unsigned)isr13, 0x08, 0x8E);
    idt_set_gate(14, (unsigned)isr14, 0x08, 0x8E);
    idt_set_gate(15, (unsigned)isr15, 0x08, 0x8E);
    idt_set_gate(16, (unsigned)isr16, 0x08, 0x8E);
    idt_set_gate(17, (unsigned)isr17, 0x08, 0x8E);
    idt_set_gate(18, (unsigned)isr18, 0x08, 0x8E);
    idt_set_gate(19, (unsigned)isr19, 0x08, 0x8E);
    idt_set_gate(20, (unsigned)isr20, 0x08, 0x8E);
    idt_set_gate(21, (unsigned)isr21, 0x08, 0x8E);
    idt_set_gate(22, (unsigned)isr22, 0x08, 0x8E);
    idt_set_gate(23, (unsigned)isr23, 0x08, 0x8E);
    idt_set_gate(24, (unsigned)isr24, 0x08, 0x8E);
    idt_set_gate(25, (unsigned)isr25, 0x08, 0x8E);
    idt_set_gate(26, (unsigned)isr26, 0x08, 0x8E);
    idt_set_gate(27, (unsigned)isr27, 0x08, 0x8E);
    idt_set_gate(28, (unsigned)isr28, 0x08, 0x8E);
    idt_set_gate(29, (unsigned)isr29, 0x08, 0x8E);
    idt_set_gate(30, (unsigned)isr30, 0x08, 0x8E);
    idt_set_gate(31, (unsigned)isr31, 0x08, 0x8E);
}

/* This is a simple string array. It contains the message that
*  corresponds to each and every exception. We get the correct
*  message by accessing like:
*  exception_message[interrupt_number] */
s8int *exception_messages[] =
{
   "Division By Zero",				//0
   "Debug",								//1
   "Non Maskable Interrupt",		//2
	"Breakpoint",						//3
 	"Into Detected Overflow", 		//4
 	"Out of Bounds",					//5
 	"Invalid Opcode",					//6
 	"No Coprocessor",					//7
 	"Double Fault",					//8
 	"Coprocessor Segment Overrun",//9
 	"Bad TSS",							//10
 	"Segment Not Present", 			//11
 	"Stack Fault",						//12
 	"General Protection Fault", 	//13
 	"Page Fault",						//14
 	"Unknown Interrupt",				//15
 	"Coprocessor Fault",				//16
 	"Alignment Check",				//17
 	"Machine Check",					//18
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved",
	"Reserved"
};

void fault_handler(struct regs *r)
{
	enable_scroll();
    /* Is this a fault whose number is from 0 to 31? */
    if (r->int_no < 32)
    {
			recursions++;
			if(recursions > 3)
			{
				write_string("\nDetected recursive faults in the fault handler!\n");
				halt();
			}

			s8int* padding = "\n                                                                               \n";
			u32int cr0;
		 	u32int cr2;
			u32int cr3;
			u32int cr4;
			__asm__ __volatile__("mov %%cr0, %0" : "=r" (cr0));
   		__asm__ __volatile__("mov %%cr2, %0" : "=r" (cr2));
			__asm__ __volatile__("mov %%cr3, %0" : "=r" (cr3));
			__asm__ __volatile__("mov %%cr4, %0" : "=r" (cr4));
			write_string("\n");
       
			settextcolor(4,0);
			write_string(exception_messages[r->int_no]);
			write_string(" Exception");
			settextcolor(15,0);
			write_string(" : Error Code = ");
			write_hex(r->err_code);
			write_string(padding);

			write_string(" EAX = ");
			write_hex(r->eax);
			write_string(" : EBX = ");
			write_hex(r->ebx);
			write_string(" : ECX = ");
			write_hex(r->ecx);
			write_string(" : EDX = ");
			write_hex(r->edx);
			write_string(padding);

			write_string(" ESI = ");
			write_hex(r->esi);
			write_string(" : EDI = ");
			write_hex(r->edi);
			write_string(" : EIP = ");
			write_hex(r->eip);
			write_string(padding);	

			write_string(" CR0 = ");
			write_hex(cr0);
			write_string(" : CR2 = ");
			write_hex(cr2);
			write_string(" : CR3 = ");
			write_hex(cr3);
			write_string(" : CR4 = ");
			write_hex(cr4);
			write_string(padding);

			write_string(" EBP = ");
			write_hex(r->ebp);
			write_string(" : ESP = ");
			write_hex(r->esp);
			write_string(padding);

			write_string(" CS = ");
			write_hex(r->cs);
			write_string(" : SS = ");
			write_hex(r->ss);
			write_string(" : DS = ");
			write_hex(r->ds);
			write_string(" : ES = ");
			write_hex(r->es);
			write_string(" : FS = ");
			write_hex(r->fs);
			write_string(" : GS = ");
			write_hex(r->gs);
			write_string(padding);

			settextcolor(4,0);
			write_string("System Halted!\n");
			halt();
    }
}
